/** ***************************************************************************
 * AppMain.hpp
 * 2012 CoffeeParty Ltd.
 * Date Created: 2012-07-21                                  format YYYY-MM-DD
 * Revision Id: $Id$
 ******************************************************************************
 */

#ifndef _APP_MAIN_HPP_
#define _APP_MAIN_HPP_

#include "CoffeeCommons.hpp"
#include "CoffeeTypes.hpp"
#include "CoffeeExyst.hpp"
#include "CoffeeLogger.hpp"
#include "CoffeeAllocator.hpp"
#include "CoffeeArray.hpp"
#include "CoffeeString.hpp"
#include "CoffeeIO.hpp"
#include "CoffeeTimer.hpp"
#include "CoffeeParty.hpp"
#include "CoffeeTexture.hpp"

#include "SugarImage.hpp"

c_double caerulean_colors[4][4] =
{
    { 0.16470588235294117, 0.3215686274509804, 0.7450980392156863, 1.0 }, ///< Cerulean Blue
    { 0.59607843137254900, 0.7058823529411765, 0.8313725490196079, 1.0 }, ///< Cerulean Pantone
    { 0.11372549019607843, 0.6745098039215687, 0.8392156862745098, 1.0 }, ///< Cerulean Crayola
    { 0.42745098039215684, 0.6078431372549019, 0.7647058823529411, 1.0 }, ///< Cerulean Frost
};

CoffeeParams gp_params;
CoffeeParty  gp_cue;

c_bool g_runstate = false;
c_string g_args = "";

c_string& getCurrentWorkingDirectory(c_string& path)
{
    path = "";
    if (getenv("PWD") != NULL)
        path = getenv("PWD");
    else c_print("Failed to get current working directory");

    return path;
}

c_void setEnvironment(c_const_string& env)
{
    putenv(const_cast<c_bytep>(env.c_str()));
}

c_void extractFilePath(c_string& path, c_string& fullpath)
{
    fullpath.replace('\\', '/');
    c_int ls = fullpath.findLast('/');
    if (ls < 0)
        return;
    path = fullpath.substring(0, ls + 1);
}


c_void createTextureFromImage(SugarImage *image, GLuint *texId, c_bool hasMipMap = false)
{
    if (!gp_cue.checkPowerOfTwo(image->getDimension()))
    {
        c_print("Image is not in the N^2");
        return;
    }

    SDL_Surface *tmp = SDL_CreateRGBSurfaceFrom(image->ark(), image->getDimension().x,
        image->getDimension().y, image->getBitsPerPixel(), (c_int)image->getPitch(),
        (c_int)image->getRedMask(), (c_int)image->getGreenMask(), (c_int)image->getBlueMask(),
        (c_int)image->getAlphaMask());

    if (tmp == NULL)
        c_print("Error occured on creating surface %s", SDL_GetError());

    glGenTextures(1, texId);
    glBindTexture(GL_TEXTURE_2D, *texId);

    if (hasMipMap)
    {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
    else
    {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }

    glTexImage2D(GL_TEXTURE_2D, 0, tmp->format->BytesPerPixel, tmp->w, tmp->h,
        0, tmp->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE,
        tmp->pixels);

    SDL_FreeSurface(tmp);

    if (gp_cue.checkError())
        c_print("Error Occured");
}

c_void deleteTexture(GLuint *texId)
{
    if (texId)
        glDeleteTextures(1, texId);
}

c_void draw2dImage(const GLuint *texId)
{
    c_const_int pad = 10;
    glBindTexture(GL_TEXTURE_2D, *texId);
    glBegin(GL_QUADS);
        glTexCoord2i(0, 1); glVertex3i(pad, pad, 0);
        glTexCoord2i(0, 0); glVertex3i(pad, gp_params.height() - pad, 0);
        glTexCoord2i(1, 0); glVertex3i(gp_params.width() - pad, gp_params.height() - pad, 0);
        glTexCoord2i(1, 1); glVertex3i(gp_params.width() - pad, pad, 0);
    glEnd();
}

c_void draw2dImage(SugarImage *image, const GLuint *texId, const c_position<c_int>& pos,
    const c_rect<c_int>& sourceRect, const c_rect<c_int>* clipRect = 0,
    c_color color = c_color(255, 255, 255, 255), c_bool useAlpha = false)
{
    if (!texId)
        return;

    if (!sourceRect.isValid())
        return;

    c_position<c_int> targetPos(pos);
    c_position<c_int> sourcePos(sourceRect.UpperLeftCorner);
    c_position<c_int> sourceSize(sourceRect.getSize());

    if (clipRect)
    {
        if (targetPos.x < clipRect->UpperLeftCorner.x)
        {
            sourceSize.x += targetPos.x - clipRect->UpperLeftCorner.x;
            if (sourceSize.x <= 0)
                return;

            sourcePos.x -= targetPos.x - clipRect->UpperLeftCorner.x;
            targetPos.x = clipRect->UpperLeftCorner.x;
        }

        if (targetPos.x + sourceSize.x > clipRect->LowerRightCorner.x)
        {
            sourceSize.x -= (targetPos.x + sourceSize.x) - clipRect->LowerRightCorner.x;
            if (sourceSize.x <= 0)
                return;
        }

        if (targetPos.y < clipRect->UpperLeftCorner.y)
        {
            sourceSize.y += targetPos.y - clipRect->UpperLeftCorner.y;
            if (sourceSize.y <= 0)
                return;

            sourcePos.y -= targetPos.y - clipRect->UpperLeftCorner.y;
            targetPos.y = clipRect->UpperLeftCorner.y;
        }

        if (targetPos.y + sourceSize.y > clipRect->LowerRightCorner.y)
        {
            sourceSize.y -= (targetPos.y + sourceSize.y) - clipRect->LowerRightCorner.y;
            if (sourceSize.y <= 0)
                return;
        }
    }

    if (targetPos.x < 0)
    {
        sourceSize.x += targetPos.x;
        if (sourceSize.x <= 0)
            return;

        sourcePos.x -= targetPos.x;
        targetPos.x = 0;
    }

    const c_dimension<c_uint>& renderTargetSize = c_dimension<c_uint>(gp_params.width(), gp_params.height());

    if (targetPos.x + sourceSize.x > (c_int)renderTargetSize.x)
    {
        sourceSize.x -= (targetPos.x + sourceSize.x) - renderTargetSize.x;
        if (sourceSize.x <= 0)
            return;
    }

    if (targetPos.y < 0)
    {
        sourceSize.x += targetPos.y;
        if (sourceSize.y <= 0)
            return;

        sourcePos.y -= targetPos.y;
        targetPos.y = 0;
    }

    if (targetPos.y + sourceSize.y > (c_int)renderTargetSize.y)
    {
        sourceSize.y -= (targetPos.y + sourceSize.y) - renderTargetSize.y;
        if (sourceSize.y <= 0)
            return;
    }

    const bool isRTT = true;
    const c_dimension<c_uint>& ss = image->getDimension();
    const c_float invW = 1.f / static_cast<c_float>(ss.x);
    const c_float invH = 1.f / static_cast<c_float>(ss.y);
    const c_rect<c_float> tcoords(sourcePos.x * invW,
        (isRTT ? (sourcePos.y + sourceSize.y) : sourcePos.y) * invH,
        (sourcePos.x + sourceSize.x) * invW,
        (isRTT ? sourcePos.y : (sourcePos.y + sourceSize.y)) * invH);

    const c_rect<c_int> poss(targetPos, sourceSize);

    glBindTexture(GL_TEXTURE_2D, *texId);
    glColor4ub(color.red(), color.green(), color.blue(), color.alpha());
    glBegin(GL_QUADS);

    glTexCoord2f(tcoords.UpperLeftCorner.x, tcoords.UpperLeftCorner.y);
    glVertex2f(GLfloat(poss.UpperLeftCorner.x), GLfloat(poss.UpperLeftCorner.y));

    glTexCoord2f(tcoords.LowerRightCorner.x, tcoords.UpperLeftCorner.y);
    glVertex2f(GLfloat(poss.LowerRightCorner.x), GLfloat(poss.UpperLeftCorner.y));

    glTexCoord2f(tcoords.LowerRightCorner.x, tcoords.LowerRightCorner.y);
    glVertex2f(GLfloat(poss.LowerRightCorner.x), GLfloat(poss.LowerRightCorner.y));

    glTexCoord2f(tcoords.UpperLeftCorner.x, tcoords.LowerRightCorner.y);
    glVertex2f(GLfloat(poss.UpperLeftCorner.x), GLfloat(poss.LowerRightCorner.y));

    glEnd();
}

c_void draw2dImage(SugarImage *image, const GLuint *texId, const c_position<c_int>& pos,
    const c_array<c_rect<c_int> >& sourceRect, c_array<c_int>& indices, const c_rect<c_int>* clipRect = 0,
    c_color color = c_color(255, 255, 255, 255), c_bool useAlpha = false)
{
    if (!texId)
        return;

    if (clipRect)
    {
        if (!clipRect->isValid())
            return;

        glEnable(GL_SCISSOR_TEST);
        const c_dimension<c_uint>& renderTargetSize = c_dimension<c_uint>(gp_params.width(), gp_params.height());
        glScissor(clipRect->UpperLeftCorner.x, renderTargetSize.y - clipRect->LowerRightCorner.y,
            clipRect->getWidth(), clipRect->getHeight());
    }


    const bool isRTT = true;
    const c_dimension<c_uint>& ss = image->getDimension();
    c_position<c_int> targetPos(pos);
    const c_float invW = 1.f / static_cast<c_float>(ss.x);
    const c_float invH = 1.f / static_cast<c_float>(ss.y);


    for (c_uint i = 0; i < indices.size(); i++)
    {

        const c_int currentIndex = indices[i];
        if (!sourceRect[currentIndex].isValid())
            return;

        const c_rect<c_float> tcoords(sourceRect[currentIndex].UpperLeftCorner.x * invW,
            (isRTT ? (sourceRect[currentIndex].LowerRightCorner.y) : sourceRect[currentIndex].UpperLeftCorner.y) * invH,
            (sourceRect[currentIndex].LowerRightCorner.x) * invW,
            (isRTT ? (sourceRect[currentIndex].UpperLeftCorner.y) : sourceRect[currentIndex].LowerRightCorner.y) * invH);
        const c_rect<c_int> poss(targetPos, sourceRect[currentIndex].getSize());

        glBindTexture(GL_TEXTURE_2D, *texId);
        glColor4ub(color.red(), color.green(), color.blue(), color.alpha());
        glBegin(GL_QUADS);

        glTexCoord2f(tcoords.UpperLeftCorner.x, tcoords.UpperLeftCorner.y);
        glVertex2f(GLfloat(poss.UpperLeftCorner.x), GLfloat(poss.UpperLeftCorner.y));

        glTexCoord2f(tcoords.LowerRightCorner.x, tcoords.UpperLeftCorner.y);
        glVertex2f(GLfloat(poss.LowerRightCorner.x), GLfloat(poss.UpperLeftCorner.y));

        glTexCoord2f(tcoords.LowerRightCorner.x, tcoords.LowerRightCorner.y);
        glVertex2f(GLfloat(poss.LowerRightCorner.x), GLfloat(poss.LowerRightCorner.y));

        glTexCoord2f(tcoords.UpperLeftCorner.x, tcoords.LowerRightCorner.y);
        glVertex2f(GLfloat(poss.UpperLeftCorner.x), GLfloat(poss.LowerRightCorner.y));

        glEnd();
        targetPos.x += sourceRect[currentIndex].getWidth();
    }

    if (clipRect)
        glDisable(GL_SCISSOR_TEST);
}

c_void createScreenshot()
{
    c_dimension<c_int> screensize(640, 480);
    SugarImage *image = new SugarRaw(screensize, 24);

    c_ubytep pixels = (c_ubytep)(image->ark());

    if (!pixels)
        return;

    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, screensize.x, screensize.y, GL_RGB, GL_UNSIGNED_BYTE, pixels);
    glReadBuffer(GL_BACK);

    c_const_int pitch = image->getPitch();
    c_ubytep p2 = pixels + (screensize.y - 1) * pitch;
    c_ubytep tmpBuffer = (c_ubytep)malloc(pitch);
    for (c_uint i = 0; i < (c_uint)screensize.y; i += 2)
    {
        memcpy(tmpBuffer, pixels, pitch);
        memcpy(pixels, p2, pitch);
        memcpy(p2, tmpBuffer, pitch);
        pixels += pitch;
        p2 -= pitch;
    }

    free(tmpBuffer);

    image->writeToDisk("./Build/tmp.bmp");

    if (image)
        drop(image);
}

#endif /** _APP_MAIN_HPP_ */

